package com.sdy.dcsb.web.callable;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sdy.common.model.BizException;
import com.sdy.common.model.Response;
import com.sdy.common.utils.Assert;
import com.sdy.common.utils.StringUtil;
import com.sdy.dcsb.biz.constants.OtherConstants;
import com.sdy.dcsb.biz.model.EsbEncapsulationParamConf;
import com.sdy.dcsb.biz.model.EsbInboundHttp;
import com.sdy.dcsb.biz.model.EsbInboundParam;
import com.sdy.dcsb.biz.model.dto.EsbScriptDto;
import com.sdy.dcsb.web.controller.inboundservice.util.HttpReqUtil;
import com.sdy.dcsb.web.util.FileUtil;
import com.sdy.dcsb.web.util.JsonUtil;
import com.sdy.dcsb.web.util.ScriptUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;

import java.io.File;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 接口封装模块请求callable
 *
 * @author Snaky
 */
@Slf4j
public class HttpEncapsulationCallable implements Callable<Response> {
    /**
     * 线程所有接口id
     */
    private List<Integer> serviceIds;
    /**
     * 线程所有接口请求方式
     */
    private List<EsbInboundHttp> inboundHttpList;
    /**
     * 线程所有接口url
     */
    private Map<Integer, String> urlMap;
    /**
     * 线程所有接口超时时间
     */
    private Map<Integer, String> overtimeMap;
    /**
     * 线程入参集合
     */
    private Map<Integer, List<EsbInboundParam>> inputParamsMap;
    /**
     * 线程出参集合
     */
    private Map<Integer, List<EsbInboundParam>> outputParamsMap;
    /**
     * 线程入参配置集合
     */
    private List<EsbEncapsulationParamConf> paramConfList;
    /**
     * 线程配置出参集合
     */
    private List<EsbInboundParam> outputParamList;
    /**
     * 查询参数字典关联信息
     */
    private Map<Integer, String> dictionaryParamIds;
    /**
     *
     */
    private Map<String, String> allParamDic;
    /**
     * callable返回数据
     */
    private JSONArray response;
    private Map<Integer, EsbScriptDto> scriptDtoMap;

    public HttpEncapsulationCallable(List<Integer> serviceIds, List<EsbInboundHttp> inboundHttpList,
                                     Map<Integer, String> urlMap,
                                     Map<Integer, String> overtimeMap,
                                     Map<Integer, List<EsbInboundParam>> inputParamsMap,
                                     Map<Integer, List<EsbInboundParam>> outputParamsMap,
                                     List<EsbEncapsulationParamConf> paramConfList,
                                     List<EsbInboundParam> outputParamList,
                                     Map<Integer, EsbScriptDto> scriptDtoMap,
                                     Map<Integer, String> dictionaryParamIds,
                                     Map<String, String> allParamDic) throws BizException {
        super();
        Assert.isNull(serviceIds, "线程关联接口为空");
        Assert.isNull(inboundHttpList, "接口请求方式为空");
        Assert.isNull(urlMap, "接口地址为空");
        Assert.isNull(overtimeMap, "接口地址为空");
        Assert.isNull(inputParamsMap, "接口入参信息为空");
        Assert.isNull(outputParamsMap, "接口出参信息为空");
        Assert.isNull(paramConfList, "接口入参配置信息为空");
        Assert.isNull(outputParamList, "接口出参配置信息为空");

        this.serviceIds = serviceIds;
        this.inboundHttpList = inboundHttpList;
        this.urlMap = urlMap;
        this.overtimeMap = overtimeMap;
        this.inputParamsMap = inputParamsMap;
        this.outputParamsMap = outputParamsMap;
        this.paramConfList = paramConfList;
        this.outputParamList = outputParamList;
        this.scriptDtoMap = scriptDtoMap;
        this.dictionaryParamIds = dictionaryParamIds;
        this.allParamDic = allParamDic;
        response = new JSONArray();
    }

    @Override
    public Response call() throws Exception {
        // 参数关联map
        Map<Integer, Integer> paramRelatedMap = paramConfList.stream().collect(Collectors
                .toMap(EsbEncapsulationParamConf::getInputParamId, EsbEncapsulationParamConf::getRelatedParamId));
        Map<Integer, List<String>> relateParamValue = new HashMap<>(paramConfList.size());

        for (Integer serviceId : serviceIds) {
            String url = urlMap.get(serviceId);
            Integer overtime = Integer.valueOf(overtimeMap.get(serviceId));
            List<EsbInboundParam> paramList = inputParamsMap.get(serviceId);
            List<EsbInboundParam> allOutputParamList = outputParamsMap.get(serviceId);

            EsbScriptDto scriptDto = scriptDtoMap.get(serviceId);
            if (scriptDto != null) {
                // 需要脚本处理接口
                // 创建临时脚本文件
                String content = scriptDto.getContent();
                if(StringUtil.isBlank(content)){
                    return Response.error("脚本内容为空");
                }
                String scriptPath = OtherConstants.PATH_SCRIPT_FILE + scriptDto.getName() + "_" + System.currentTimeMillis() + ".js";
                File tempFile =  FileUtil.save(content, scriptPath);
                if (!tempFile.exists()) {
                    return Response.error("脚本创建错误");
                }
                int defaultNum = 1;
                for (int i = 0; i < defaultNum; i++) {
                    int size = paramList == null ? 0 : paramList.size();
                    if (size > 0) {
                        Map<String, String> paramMap = new ConcurrentHashMap<>(size);
                        for (EsbInboundParam inboundParam : paramList) {
                            // 获取关联的参数值
                            initParamValue(defaultNum, i, paramRelatedMap, relateParamValue, inboundParam);
                            if (paramRelatedMap.containsKey(inboundParam.getId()) && relateParamValue.containsKey(inboundParam.getId())) {
                                List<String> array = relateParamValue.get(inboundParam.getId());
                                defaultNum = array.size();
                                if (defaultNum > 1) {
                                    paramMap.put(inboundParam.getParamKey(), array.get(i));
                                } else {
                                    paramMap.put(inboundParam.getParamKey(), array.get(0));
                                }
                            } else {
                                String value = inboundParam.getParamValue();
                                paramMap.put(inboundParam.getParamKey(), value == null ? "" : value);
                            }
                        }
                        paramMap.put("serviceUrl", url);
                        Response<JSONArray> scriptResp = ScriptUtil.invokeFunction(tempFile.toPath(), scriptDto.getFuncName(), paramMap);
                        if (!scriptResp.getSuccess()) {
                            return scriptResp;
                        }
                        JSONArray scriptArray = scriptResp.getData();
                        saveRelateParamValue(paramRelatedMap, allOutputParamList, relateParamValue, scriptArray,null);
                        JSONArray responseArray = saveOutputParamValue(serviceId, outputParamList, scriptArray,null);
                        resultMerge(responseArray, defaultNum, i);
                    }
                }
                boolean flag = tempFile.delete();
                if(!flag){
                    log.error(OtherConstants.PREFIX_LOG + "临时脚本文件删除失败：" + scriptPath);
                }
            } else {
                // json入参
                JSONObject reqJson = null;
                // form-data入参
                List<Part> parts = null;
                // urlencoded入参
                List<NameValuePair> param = null;
                // 默认data
                int defaultNum = 1;
                for (int i = 0; i < defaultNum; i++) {
                    JSONArray responseArray = new JSONArray();
                    int size = paramList == null ? 0 : paramList.size();
                    Map<String, String> headerMap = null;
                    if (size > 0) {
                        headerMap = new HashMap<>(size);
                        for (EsbInboundParam inboundParam : paramList) {
                            // 获取关联的参数值
                            initParamValue(defaultNum, i, paramRelatedMap, relateParamValue, inboundParam);
                            // 参数格式(1 地址栏参数、2 head参数、3 json格式、4 form-data参数、5 form-urlencoded参数)
                            Integer paramFormat = inboundParam.getParamFormat();
                            String value = inboundParam.getParamValue();
                            value = value == null ? "" : value;
                            switch (paramFormat) {
                                case 1:
                                    url = concatUrl(url, inboundParam.getParamKey(), value);
                                    break;
                                case 2:
                                    headerMap.put(inboundParam.getParamKey(), value);
                                    break;
                                case 3:
                                    if (reqJson == null) {
                                        reqJson = new JSONObject();
                                    }
                                    if (inboundParam.getParentOrderNum() == null) {
                                        reqJson.put(inboundParam.getParamKey(), value);
                                    }
                                    break;
                                case 4:
                                    if (parts == null) {
                                        parts = new ArrayList<>();
                                    }
                                    parts.add(new StringPart(inboundParam.getParamKey(), value, "utf-8"));
                                    break;
                                case 5:
                                    if (param == null) {
                                        param = new ArrayList<>();
                                    }
                                    param.add(new NameValuePair(inboundParam.getParamKey(), value));
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    String result;
                    if (reqJson != null) {
                        result = HttpReqUtil.postJson(url, reqJson.toString(), "utf-8", "utf-8", 30000, headerMap);
                    } else if (param != null) {
                        result = HttpReqUtil.postUrlencoded(url, param.toArray(new NameValuePair[param.size()]),
                                "utf-8", "utf-8", overtime, headerMap);
                    } else if (parts != null) {
                        result = HttpReqUtil.postMultipart(url, parts.toArray(new Part[parts.size()]),
                                "utf-8", "utf-8", overtime, headerMap);
                    } else {
                        Integer httpType = inboundHttpList.stream().filter(esbInboundHttp -> esbInboundHttp.getServiceid().equals(serviceId))
                                .findFirst().get().getHttptype();
                        if (httpType == 1) {
                            result = HttpReqUtil.get(url, "utf-8", overtime, headerMap);
                        } else {
                            result = HttpReqUtil.postUrlencoded(url, null, "utf-8", "utf-8", overtime, headerMap);
                        }
                    }
                    if(!result.startsWith("{")){
                        // 非json格式，直接返回错误信息，一般为权限异常
                        return Response.error(result);
                    }
                    JSONObject resultJson = JSONObject.parseObject(result);
                    if (resultJson == null) {
                        return Response.success("暂未查询到数据");
                    }
                    // 保存存在关联的参数值、所需出参
                    if (allOutputParamList != null) {
                        Optional<EsbInboundParam> first = allOutputParamList.stream().filter(esbInboundParam -> esbInboundParam.getNodeType() >= 4).findFirst();
                        Integer nodeType;
                        String dataKey;
                        if (first != null && first.isPresent()) {
                            EsbInboundParam esbInboundParam = first.get();
                            nodeType = esbInboundParam.getNodeType();
                            dataKey = esbInboundParam.getParamKey();
                            if (nodeType == 4) {
                                // JSONObject 格式
                                JSONObject data = resultJson.getJSONObject(dataKey);
                                if (data != null) {
                                    saveRelateParamValue(paramRelatedMap, allOutputParamList, relateParamValue, data, resultJson);
                                    JSONArray array = new JSONArray();
                                    array.add(data);
                                    responseArray = saveOutputParamValue(serviceId, outputParamList, array, resultJson);
                                }
                            } else {
                                // JSONArray 格式
                                JSONArray data = resultJson.getJSONArray(dataKey);
                                if (data != null) {
                                    saveRelateParamValue(paramRelatedMap, allOutputParamList, relateParamValue, data, resultJson);
                                    // 保存所需参数
                                    responseArray = saveOutputParamValue(serviceId, outputParamList, data, resultJson);
                                }
                            }
                        } else {
                            // 所有参数都在第一层
                            saveRelateParamValue(paramRelatedMap, allOutputParamList, relateParamValue, null, resultJson);
                            responseArray = saveOutputParamValue(serviceId, outputParamList, null,resultJson);
                        }
                    }
                    resultMerge(responseArray, defaultNum, i);
                }
            }
        }
        return Response.success(response);
    }

    private String concatUrl(String url, String key, String value) {
        try {
            int index = url.indexOf("?");
            if (index >= 0) {
                // 防止重复添加参数
                String[] params = url.substring(index + 1).split("[&=]");
                List<String> paramList = Arrays.asList(params);
                if (!paramList.contains(key)) {
                    if(StringUtil.isBlank(value)){
                        url += "&" + key + "=" + value;
                    } else {
                        url += "&" + key + "=" + URLEncoder.encode(value, "utf-8");
                    }
                }
            } else {
                if(StringUtil.isBlank(value)){
                    url += "?" + key + "=" + value;
                } else {
                    url += "?" + key + "=" + URLEncoder.encode(value, "utf-8");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return url;
    }

    /**
     * 保存存在关联的参数值
     *
     * @param paramRelatedMap  存在关联关系的参数id map
     * @param outputParamList  本次查询的接口对应所有出参集合
     * @param relateParamValue 关联参数值map
     * @param data             接口返回业务数据，用于获取关联参数值
     * @param resultJson       接口返回所有数据，用于获取关联参数值,为空时，表示脚本调用
     */
    private void saveRelateParamValue(Map<Integer, Integer> paramRelatedMap, List<EsbInboundParam> outputParamList, Map<Integer,
            List<String>> relateParamValue, JSON data, JSONObject resultJson) {

        for (Map.Entry<Integer, Integer> entry : paramRelatedMap.entrySet()) {
            Integer paramId = entry.getKey();
            Integer relatedParamId = entry.getValue();

            Optional<EsbInboundParam> first = outputParamList.stream().filter(esbInboundParam
                    -> esbInboundParam.getId().equals(relatedParamId)).findFirst();
            if (first != null && first.isPresent()) {
                EsbInboundParam inboundParam = first.get();
                List<String> paramValues;
                // 初始化参数值集合
                if (relateParamValue.containsKey(paramId)) {
                    paramValues = relateParamValue.get(paramId);
                } else {
                    paramValues = new ArrayList<>();
                }
                if(inboundParam.getParentOrderNum() != null && data != null){
                    // 第二节点下参数
                    if (data instanceof JSONObject) {
                        paramValues.add(((JSONObject) data).getString(inboundParam.getParamKey()));
                    } else if (data instanceof JSONArray) {
                        for (Object obj : (JSONArray) data) {
                            JSONObject json = (JSONObject) obj;
                            paramValues.add(json.getString(inboundParam.getParamKey()));
                        }
                    }
                    relateParamValue.put(paramId, paramValues);
                } else {
                    // 第一节点参数
                    if(resultJson != null){
                        paramValues.add(resultJson.getString(inboundParam.getParamKey()));
                        relateParamValue.put(paramId, paramValues);
                    }
                }

            }
        }
    }

    /**
     * 保存所需参数值
     *
     * @param serviceId       接口id；
     * @param outputParamList 出参集合
     * @param data            接口返回业务数据
     * @param resultJson      接口返回所有数据，为空时，表示脚本调用
     */
    private JSONArray saveOutputParamValue(Integer serviceId, List<EsbInboundParam> outputParamList, JSONArray data, JSONObject resultJson) {

        List<EsbInboundParam> collect = outputParamList.stream().filter(esbInboundParam -> esbInboundParam.getServiceId().equals(serviceId)).collect(Collectors.toList());
        if (collect != null) {
            // 临时存储所需参数值的结果集
            JSONArray responseArray = new JSONArray();
            if(data != null){
                // 存在第二层节点
                for (int i = 0; i < data.size(); i++) {
                    JSONObject json = new JSONObject();
                    JSONObject dataI = data.getJSONObject(i);
                    // 判断是否存在key-value，防止添加空json
                    if (dataI.size() > 0) {
                        for (EsbInboundParam inboundParam : collect) {
                            if(inboundParam.getParentOrderNum() != null){
                                // 第二节点参数
                                if (inboundParam.getServiceId().equals(serviceId)) {
                                    String key = inboundParam.getParamKey();
                                    if (dataI.containsKey(key)) {
                                        json.put(key, getDicValue(inboundParam.getId(),dataI.getString(key)));
                                    }
                                }
                            } else {
                                // 第一节点参数
                                if(resultJson != null){
                                    String key = inboundParam.getParamKey();
                                    json.put(key, getDicValue(inboundParam.getId(),resultJson.getString(key)));
                                }
                            }

                        }
                        responseArray.add(json);
                    }
                }
            } else {
                // 参数全在第一节点
                JSONObject json = new JSONObject();
                for (EsbInboundParam inboundParam : collect) {
                    String key = inboundParam.getParamKey();
                    json.put(key,getDicValue(inboundParam.getId(),resultJson.getString(key)));
                }
                responseArray.add(json);
            }

            return responseArray;
        }
        return null;
    }

    /**
     * 初始化接口参数值
     *
     * @param defaultNum       默认循环次数
     * @param i                循环变量
     * @param paramRelatedMap  参数关联map
     * @param relateParamValue 存在关联性的参数值
     * @param inboundParam     接口参数
     */
    private void initParamValue(int defaultNum, int i, Map<Integer, Integer> paramRelatedMap, Map<Integer, List<String>> relateParamValue, EsbInboundParam inboundParam) {
        if (paramRelatedMap.containsKey(inboundParam.getId()) && relateParamValue.containsKey(inboundParam.getId())) {
            List<String> array = relateParamValue.get(inboundParam.getId());
            defaultNum = array.size();
            if (defaultNum > 1) {
                inboundParam.setParamValue(array.get(i));
            } else if(defaultNum == 1){
                inboundParam.setParamValue(array.get(0));
            } else {
                inboundParam.setParamValue("");
            }
        }
    }

    /**
     * 接口数据合并
     *
     * @param responseArray 接口返回数据
     * @param defaultNum    默认循环次数
     * @param i             循环控制
     */
    private void resultMerge(JSONArray responseArray, int defaultNum, int i) {
        if (responseArray != null && responseArray.size() > 0) {
            if (defaultNum == 1) {
                response = JsonUtil.mergeArray(responseArray, response);
            } else {
                response.getJSONObject(i).putAll(responseArray.getJSONObject(0));
            }
        } else {
            response.clear();
        }
    }

    private String getDicValue(Integer paramId, String paramValue){
        if(StringUtil.isBlank(paramValue)){
            return paramValue;
        }
        if(dictionaryParamIds == null || !dictionaryParamIds.containsKey(paramId)){
            return paramValue;
        }
        String dicKey = dictionaryParamIds.get(paramId).concat("&").concat(paramValue);
        if(allParamDic == null || !allParamDic.containsKey(dicKey)){
            return paramValue;
        }
        String value = allParamDic.get(dicKey);
        return paramValue.concat("(").concat(value).concat(")");
    }
}
